<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Chapter&nbsp;11.&nbsp;任务管理</title>
    <link rel="stylesheet" href="css/html.css" type="text/css">
    <meta name="generator" content="DocBook XSL Stylesheets V1.65.1">
    <link rel="home" href="index.html" title="JBoss jBPM 3.1">
    <link rel="up" href="index.html" title="JBoss jBPM 3.1">
    <link rel="previous" href="context.html" title="Chapter&nbsp;10.&nbsp;Context">
    <link rel="next" href="scheduler.html" title="Chapter&nbsp;12.&nbsp;Scheduler">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
    <table width="100%" summary="Navigation header">
        <tr>
            <th colspan="3" align="center">Chapter&nbsp;11.&nbsp;任务管理</th>
        </tr>
        <tr>
            <td width="20%" align="left"><a accesskey="p" href="context.html">Prev</a>&nbsp;</td>
            <th width="60%" align="center">&nbsp;</th>
            <td width="20%" align="right">&nbsp;<a accesskey="n" href="scheduler.html">Next</a></td>
        </tr>
    </table>
    <hr>
</div>
<div class="chapter" lang="cn">
<div class="titlepage">
    <div>
        <div><h2 class="title"><a name="taskmanagement"></a>Chapter&nbsp;11.&nbsp;任务管理</h2></div>
    </div>
    <div></div>
</div>
<p>jBPM的核心业务能力是能够持久化流程执行状态。这种特性极为有用的一个方面就是为人们管理任务和任务列表。
    jBPM允许为人们的任务指定一小块程序，该程序块描述整个有等待状态的流程。</p>

<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="tasks"></a>11.1.&nbsp;任务（Tasks）</h2></div>
        </div>
        <div></div>
    </div>
    <p>Tasks are part of the process definition and they define how
        task instances must be created and assigned during process
        executions.</p>

    <p>Tasks can be defined in <tt class="literal">task-node</tt>s and in the
        <tt class="literal">process-definition</tt>. The most common way is to define
        one or more <tt class="literal">task</tt>s in a <tt class="literal">task-node</tt>.
        In that case the <tt class="literal">task-node</tt> represents a task to be
        done by the user and the process execution should wait until the actor
        completes the task. When the actor completes the task, process execution
        should continue. When more tasks are specified in a
        <tt class="literal">task-node</tt>, the default behaviour is to wait for all the
        tasks to complete.
    </p>

    <p>Tasks can also be specified on the <tt class="literal">process-definition</tt>.
        Tasks specified on the process definition can be looked up by name and
        referenced from within <tt class="literal">task-node</tt>s or used from
        inside actions. In fact, all tasks (also in task-nodes) that are given a
        name can be looked up by name in the process-definition.
    </p>

    <p>Task names must be unique in the whole process definition. Tasks
        can be given a <tt class="literal">priority</tt>. This priority will be used
        as the initial priority for each task instance that is created for this task.
        TaskInstances can change this initial priority afterwards.</p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="taskinstances"></a>11.2.&nbsp;任务实例</h2></div>
        </div>
        <div></div>
    </div>
    <p>A task instance can be assigned to an actorId (java.lang.String). All task instances
        are stored in one table of the database (JBPM_TASKINSTANCE). By querying this table for
        all task instances for a given actorId, you get the task list for that perticular user.
    </p>

    <p>The jBPM task list mechanism can combine jBPM tasks with other tasks, even when those
        tasks are unrelated to a process execution. That way jBPM developers can easily combine
        jBPM-process-tasks with tasks of other applications in one centralized task-list-repository.
    </p>

    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="taskinstancelifecycle"></a>11.2.1.&nbsp;Task instance lifecycle</h3>
                </div>
            </div>
            <div></div>
        </div>
        <p>The task instance lifecycle is straightforward: After creation, task instances can optionally
            be started. Then, task instances can be ended, which means that the task instance is marked as
            completed.</p>

        <p>Note that for flexibility, assignment is not part of the life cycle. So task instances
            can be assigned or not assigned. Task instance assignment does not have an influence on the
            task instance life cycle.</p>

        <p>Task instances are typically created by the process execution entering a
            <tt class="literal">task-node</tt> (with the method <tt class="literal">TaskMgmtInstance.createTaskInstance(...)</tt>).
            Then, a user interface component will query the database for the tasklists using the
            <tt class="literal">TaskMgmtSession.findTaskInstancesByActorId(...)</tt>. Then, after collecting input
            from the user, the UI component calls <tt class="literal">TaskInstance.assign(String)</tt>,
            <tt class="literal">TaskInstance.start()</tt> or <tt class="literal">TaskInstance.end(...)</tt>.
        </p>

        <p>A task instance maintains it's state by means of date-properties : <tt class="literal">create</tt>,
            <tt class="literal">start</tt> and <tt class="literal">end</tt>. Those properties can be accessed by
            their respective getters on the <tt class="literal">TaskInstance</tt>.</p>

        <p>Currently, completed task instances are marked with an end date so that they are not
            fetched with subsequent queries for tasks lists. But they remain in the JBPM_TASKINSTANCE
            table.</p></div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="taskinstancesandgraphexecution"></a>11.2.2.&nbsp;Task instances and
                    graph execution</h3></div>
            </div>
            <div></div>
        </div>
        <p>Task instances are the items in an actor's tasklist. Task instances
            can be signalling. A signalling task instance is a task instance that, when
            completed, can send a signal to its token to continue the process execution.
            Task instances can be blocking, meaning that the related token (=path of
            execution) is not allowed to leave the task-node before the task instance is
            completed. By default task instances are signalling and non-blocking.
        </p>

        <p>In case more than one task instance are associated with a task-node,
            the process developer can specify how completion of the task instances affects
            continuation of the process. Following is the list of values that can be
            given to the signal-property of a task-node.</p>

        <div class="itemizedlist">
            <ul type="disc">
                <li><span class="bold"><b>last</b></span>: This is the default. Proceeds execution when the last task
                    instance is completed. When no tasks are created on entrance of this node, execution is continued.
                </li>
                <li><span class="bold"><b>last-wait</b></span>: Proceeds execution when the last task instance is
                    completed. When no tasks are created on entrance of this node, execution waits in the task node till
                    tasks are created.
                </li>
                <li><span class="bold"><b>first</b></span>: Proceeds execution when the first task instance is
                    completed. When no tasks are created on entrance of this node, execution is continued.
                </li>
                <li><span class="bold"><b>first-wait</b></span>: Proceeds execution when the first task instance is
                    completed. When no tasks are created on entrance of this node, execution waits in the task node till
                    tasks are created.
                </li>
                <li><span class="bold"><b>unsynchronized</b></span>: Execution always continues, regardless wether tasks
                    are created or still unfinished.
                </li>
                <li><span class="bold"><b>never</b></span>: Execution never continues, regardless wether tasks are
                    created or still unfinished.
                </li>
            </ul>
        </div>
        <p>Task instance creation might be based upon a runtime calculation. In that case, add an
            <tt class="literal">ActionHandler</tt> on the <tt class="literal">node-enter</tt> event of the <tt
                class="literal">task-node</tt> and
            set the attribute <tt class="literal">create-tasks="false"</tt>. Here is an example of such an action
            handler implementation:
        </p><pre class="programlisting">public class CreateTasks implements ActionHandler {
  public void execute(ExecutionContext executionContext) throws Exception {
    Token token = executionContext.getToken();
    TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
      
    TaskNode taskNode = (TaskNode) executionContext.getNode();
    Task changeNappy = taskNode.getTask("change nappy");

    // now, 2 task instances are created for the same task.
    tmi.createTaskInstance(changeNappy, token);
    tmi.createTaskInstance(changeNappy, token);
  }
}</pre>
        <p>As shown in the example the tasks to be created can be specified
            in the task-node. They could also be specified in the
            <tt class="literal">process-definition</tt> and fetched from the
            <tt class="literal">TaskMgmtDefinition</tt>. <tt class="literal">TaskMgmtDefinition</tt> extends
            the ProcessDefinition with task management information.</p>

        <p>The API method for marking task instances as completed is
            <tt class="literal">TaskInstance.end()</tt>. Optionally, you can specify a transition
            in the end method. In case the completion of this task instance triggers
            continuation of the execution, the task-node is left over the specified
            transition.</p></div>
</div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="assignment"></a>11.3.&nbsp;分派（Assignment）</h2></div>
        </div>
        <div></div>
    </div>
    <p>A process definition contains task nodes. A <tt class="literal">task-node</tt>
        contains zero or more tasks. Tasks are a static description as part of the process
        definition. At runtime, tasks result in the creation of task instances. A task
        instance corresponds to one entry in a person's task list.</p>

    <p>With jBPM, <a href="taskmanagement.html#pushmodel" title="11.3.3.&nbsp;Push model">push</a> and <a
            href="taskmanagement.html#pullmodel" title="11.3.4.&nbsp;Pull model">pull</a>
        model (see below) of task assignment can be applied in combination.
        The process can calculate the responsible for a task and push it in his/her tasklist.
        Or alternatively, a task can be assigned to a pool of actors, in which case
        each of the actors in the pool can pull the task and put it in the actor's
        personal tasklist.</p>

    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="assignmentinterfaces"></a>11.3.1.&nbsp;Assignment interfaces</h3></div>
            </div>
            <div></div>
        </div>
        <p>Assigning task instances is done via the interface
            <tt class="literal">AssignmentHandler</tt>:</p><pre class="programlisting">public interface AssignmentHandler extends Serializable {
  void assign( Assignable assignable, ExecutionContext executionContext );
}</pre>
        <p>An assignment handler implementation is called when a task instance is
            created. At that time, the task instance can be assigned to one or more actors.
            The <tt class="literal">AssignmentHandler</tt> implementation should
            call the <tt class="literal">Assignable</tt> methods (<tt class="literal">setActorId</tt> or
            <tt class="literal">setPooledActors</tt>) to assign a task. The <tt class="literal">Assignable</tt> is
            either
            a <tt class="literal">TaskInstance</tt> or a <tt class="literal">SwimlaneInstance</tt> (=process role).
        </p><pre class="programlisting">public interface Assignable {
  public void setActorId(String actorId);
  public void setPooledActors(String[] pooledActors);
}</pre>
        <p>Both <tt class="literal">TaskInstance</tt>s
            and <tt class="literal">SwimlaneInstance</tt>s can be assigned to a specific user or to a pool
            of actors. To assign a TaskInstance to a user, call
            <tt class="literal">Assignable.setActorId(String actorId)</tt>. To assign a TaskInstance to a pool
            of candidate actors, call <tt class="literal">Assignable.setPooledActors(String[] actorIds)</tt>.
        </p>

        <p>Each task in the process definition can be associated with an assignment
            handler implementation to perform the assignment at runtime.</p>

        <p>When more then one task in a process should be assigned to the same person
            or group of actors, consider the usage of a <a href="taskmanagement.html#swimlanes"
                                                           title="11.6.&nbsp;泳道（swimlane）">泳道（swimlane）</a></p>

        <p>To allow for the creation of reusable <tt class="literal">AssignmentHandler</tt>s,
            each usage of an <tt class="literal">AssignmentHandler</tt> can be configured in
            the <tt class="literal">processdefinition.xml</tt>. See <a href="jpdl.html#delegation"
                                                                       title="16.2.&nbsp;Delegation">Section&nbsp;16.2,
            &#8220;Delegation&#8221;</a>
            for more information on how to add configuration to assignment handlers.</p></div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="theassignmentdatamodel"></a>11.3.2.&nbsp;The assignment data model</h3>
                </div>
            </div>
            <div></div>
        </div>
        <p>The datamodel for managing assignments of task instances and swimlane instances to
            actors is the following. Each <tt class="literal">TaskInstance</tt> has an actorId
            and a set of pooled actors.</p>

        <div class="figure"><a name="assignment.model.image"></a>

            <div class="mediaobject" align="center"><img src="images/assignment.model.gif" align="middle"
                                                         alt="The assignment model class diagram"></div>
            <p class="title"><b>Figure&nbsp;11.1.&nbsp;The assignment model class diagram</b></p></div>
        <p>The actorId is the responsible for the task, while the
            set of pooled actors represents a collection of candidates that can become responsible
            if they would take the task. Both actorId and pooledActors are optional and can
            also be combined.</p></div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="pushmodel"></a>11.3.3.&nbsp;Push model</h3></div>
            </div>
            <div></div>
        </div>
        <p>The actorId of a task instance denotes the responsible for that given task.
            The pooled actors for a <tt class="literal">TaskInstance</tt> are the candidate actors for
            that task. Typically, the actorId of a <tt class="literal">TaskInstance</tt> will reference a user.
            The pooled actors can reference users and/or groups.</p>

        <p>The personal task list of a user are all the <tt class="literal">TaskInstance</tt>s
            that have the given user as <tt class="literal">actorId</tt>. This list can be obtained
            with <tt class="literal">TaskMgmtSession.findTaskInstances(String actorId)</tt>.
        </p></div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="pullmodel"></a>11.3.4.&nbsp;Pull model</h3></div>
            </div>
            <div></div>
        </div>
        <p>On the other
            hand, the tasks of pooled tasks for a given user are the tasks for which the given user is
            referenced in the pooled actors. Fetching the list of pooled tasks is typically a
            two step operation : 1) get all the groups for the given user from the identity component
            and 2) get the list of all pooled tasks for the combined set of the user's actorId and
            the actorId's that reference the users' groups. Getting the list of pooled tasks that are
            offered to a given user can be done with the methods
            <tt class="literal">TaskMgmtSession.findPooledTaskInstances(String actorId)</tt> or
            <tt class="literal">TaskMgmtSession.findPooledTaskInstances(List actorIds)</tt>. These methods
            will only return task instances for which the actorId is <tt class="literal">null</tt> and
            one of the given actorIds matches one of the pooled actors.
        </p>

        <p>To prevent multiple users working on the same pooled task, it is sufficient
            to update the actorId of the <tt class="literal">TaskInstance</tt> with the user's actorId.
            After that, the task instance will not appear in the list of pooled tasks, but only
            in the user's personal task list. Setting the actorId of a taskInstance to
            <tt class="literal">null</tt> will put the task instance back in the pooled tasks.</p></div>
</div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="taskinstancevariables"></a>11.4.&nbsp;任务实例变量</h2></div>
        </div>
        <div></div>
    </div>
    <p>A task instance can have its own set of variables and a task instance can
        also 'see' the process variables. Task instances are usually created in an execution
        path (=token). This creates a parent-child relation between the token and the task
        instance similar to the parent-child relation between the tokens themselves.
        The normal scoping rules apply between the variables of a task instance and the
        process variables of the related token. More info about scoping can be found
        in <a href="context.html#variablescopes" title="10.4.&nbsp;Variables scopes">Section&nbsp;10.4, &#8220;Variables
        scopes&#8221;</a>.
    </p>

    <p>This means that a task instance can 'see' its own variables plus all the variables
        of its related token.
    </p>

    <p>The controller can be used to create populate and submit variables between
        the task instance scope and the process scoped variables.
    </p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="taskcontrollers"></a>11.5.&nbsp;任务控制器</h2>
            </div>
        </div>
        <div></div>
    </div>
    <p>At creation of a task instance, the task controllers can populate the task instance variables
        and when the task instance is finished, the task controller can submit the data of the
        task instance into the process variables.
    </p>

    <p>Note that you are not forced to use task controllers. Task instances also are able
        to 'see' the process variables related to its token. Use task controllers when you want
        to:
    </p>

    <div class="itemizedlist">
        <ul type="disc">
            <li>a) create copies of variables in the task instances so that intermediate updates
                to the task instance variables don't affect the process variables untill the process
                is finished and the copies are submitted back into the process variables.
            </li>
            <li>b) the task instance variables do not relate one-on-one with the process variables.
                E.g. suppose the process has variables 'sales in januari' 'sales in februari' and 'sales in march'.
                Then the form for the task instance might need to show the average sales in the 3 months.
            </li>
        </ul>
    </div>
    <p>Tasks are intended to collect input from users. But there are many user interfaces
        which could be used to present the tasks to the users. E.g. a web application, a swing application,
        an instant messenger, an email form,... So the task controllers make the bridge between the
        process variables (=process context) and the user interface application. The task controllers
        provide a view of process variables to the user interface application.
    </p>

    <p>The task controller makes the translation (if any) from the process variables to the
        task variables. When a task instance is created, the task controller is responsible for extracting
        information from the process variables and creating the task variables. The task variables serve
        as the input for the user interface form. And the user input can be stored in the task variables.
        When the user ends the task, the task controller is responsible for updating the process
        variables based on the task instance data.
    </p>

    <div class="figure"><a name="task.controllers.image"></a>

        <div class="mediaobject" align="center"><img src="images/task.controllers.jpg" align="middle"
                                                     alt="The task controllers"></div>
        <p class="title"><b>Figure&nbsp;11.2.&nbsp;The task controllers</b></p></div>
    <p>In a simple scenario, there is a one-on-one mapping between process variables and the
        form parameters. Task controllers are specified in a task element.
        In this case, the default jBPM task controller can be used and it takes a list of
        <tt class="literal">variable</tt> elements inside. The variable elements express
        how the process variables are copied in the task variables.
    </p>

    <p>The next example shows how you can create separate task instance variable copies
        based on the process variables:
    </p><pre class="programlisting">&lt;task name="clean ceiling"&gt;
  &lt;controller&gt;
    &lt;variable name="a" access="read" mapped-name="x" /&gt;
    &lt;variable name="b" access="read,write,required" mapped-name="y" /&gt;
    &lt;variable name="c" access="read,write" /&gt;
  &lt;/controller&gt;
&lt;/task&gt;</pre>
    <p>The <tt class="literal">name</tt> attribute refers to the name of the process variable.
        The <tt class="literal">mapped-name</tt> is optional and refers to the name of the task instance
        variable. If the mapped-name attribute is omitted, mapped-name defaults to the name.
        Note that the mapped-name also is used as the label for the fields in the task instance
        form of the web application.
    </p>

    <p>
        The <tt class="literal">access</tt> attribute specifies if the variable is copied at task instance
        creation, will be written back to the process variables at task end and wether it is
        required. This information can be used by the user interface to generate the proper
        form controls. The access attribute is optional and the default
        access is 'read,write'.
    </p>

    <p>A <tt class="literal">task-node</tt> can have many tasks and a <tt class="literal">start-state</tt> can have
        1 task.
    </p>

    <p>If the simple one-to-one mapping between process variables and form parameters is too limiting, you
        can also write your own TaskControllerHandler implementation. Here's the TaskControllerHandler interface:
    </p><pre class="programlisting">public interface TaskControllerHandler extends Serializable {
  void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
  void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}</pre>
    <p>And here's how to configure your custom task controller implementation in a task:</p><pre class="programlisting">&lt;task name="clean ceiling"&gt;
  &lt;controller class="com.yourcom.CleanCeilingTaskControllerHandler"&gt;
    -- here goes your task controller handler configuration --
  &lt;/controller&gt;
&lt;/task&gt;</pre>
</div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="swimlanes"></a>11.6.&nbsp;泳道（Swimlanes）</h2></div>
        </div>
        <div></div>
    </div>
    <p>A swimlane is a process role. It is a mechanism to specify that
        multiple tasks in the process should be done by the same actor. So
        after the first task instance is created for a given swimlane, the actor
        should be remembered in the process for all subsequent tasks that are in
        the same swimlane. A swimlane therefore has one
        <a href="taskmanagement.html#assignment" title="11.3.&nbsp;Assignment"><tt class="literal">assignment</tt></a>
        and all tasks that reference a swimlane should not specify an
        <a href="taskmanagement.html#assignment" title="11.3.&nbsp;Assignment"><tt class="literal">assignment</tt></a>.
    </p>

    <p>When the first task in a given swimlane is created, the
        <tt class="literal">AssignmentHandler</tt> of the swimlane is called.
        The <tt class="literal">Assignable</tt> that is passed to the <tt class="literal">AssignmentHandler</tt>
        will be the <tt class="literal">SwimlaneInstance</tt>. Important to know is that
        all assignments that are done on the task instances in a given swimlane will
        propagate to the swimlane instance. This behaviour is implemented as the default
        because the person that takes a task to fulfilling a certain process role will
        have the knowledge of that perticular process. So all subsequent assignements
        of task instances to that swimlane are done automatically to that user.
    </p>

    <p>Swimlane is a terminology borrowed from UML activity diagrams.
    </p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="swimlaneinstarttask"></a>11.7.&nbsp;开始任务中的泳道</h2></div>
        </div>
        <div></div>
    </div>
    <p>A swimlane can be associated with the start task to capture the process
        initiator.</p>

    <p>A task can be specified in a start-state. That task be associated with a
        swimlane. When a new task instance is created for such a task, the current
        authenticated actor will be captured with
        <a href="security.html#authentication" title="17.2.&nbsp;Authentication">Authentication.getAuthenticatedActorId()</a>
        and that actor will be stored in the swimlane of the start task.
    </p>

    <p>For example:</p><pre class="programlisting">&lt;process-definition&gt;
  &lt;swimlane name='initiator' /&gt;
  &lt;start-state&gt;
    &lt;task swimlane='initiator' /&gt;
    &lt;transition to='...' /&gt;
  &lt;/start-state&gt;
  ...
&lt;/process-definition&gt;</pre>
    <p>Also variables can be added to the start task as with any other task to
        define the form associated with the task. See <a href="taskmanagement.html#taskcontrollers"
                                                         title="11.5.&nbsp;Task controllers">Section&nbsp;11.5, &#8220;Task
        controllers&#8221;</a></p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="taskevents"></a>11.8.&nbsp;任务事件</h2></div>
        </div>
        <div></div>
    </div>
    <p>Tasks can have actions associated with them. There are 4 standard event types defined
        for tasks: <tt class="literal">task-create</tt>,
        <tt class="literal">task-assign</tt>, <tt class="literal">task-start</tt> and <tt class="literal">task-end</tt>.
    </p>

    <p><tt class="literal">task-create</tt> is fired when a task instance is created.</p>

    <p><tt class="literal">task-assign</tt> is fired when a task instance is being assigned.
        Note that in actions that are executed on this event, you can access the previous
        actor with <tt class="literal">executionContext.getTaskInstance().getPreviousActorId();</tt>
    </p>

    <p><tt class="literal">task-start</tt> is fired when <tt class="literal">TaskInstance.start()</tt>
        is called. This can be used to indicate that the user is actually starting to work on this
        task instance. Starting a task is optional.</p>

    <p><tt class="literal">task-end</tt> is fired when <tt class="literal">TaskInstance.end(...)</tt>
        is called. This marks the completion of the task. If the task is related to a process
        execution, this call might trigger the resuming of the process execution.
    </p>

    <p>Since tasks can have events and actions associated with them, also
        exception handlers can be specified on a task. For more information about exception
        handling, see <a href="processmodelling.html#exceptionhandling" title="9.7.&nbsp;Exception handling">Section&nbsp;9.7,
        &#8220;Exception handling&#8221;</a>.</p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="tasktimers"></a>11.9.&nbsp;任务定时器</h2></div>
        </div>
        <div></div>
    </div>
    <p>As on nodes, timers can be specified on tasks. See <a href="scheduler.html#timers" title="12.1.&nbsp;Timers">Section&nbsp;12.1,
        &#8220;Timers&#8221;</a>.</p>

    <p>The special thing about timers for tasks is that the <tt class="literal">cancel-event</tt>
        for task timers can be customized. By default, a timer on a task will be cancelled when
        the task is ended (=completed). But with the <tt class="literal">cancel-event</tt> attribute
        on the timer, process developers can customize that to e.g. <tt class="literal">task-assign</tt>
        or <tt class="literal">task-start</tt>. The <tt class="literal">cancel-event</tt> supports multiple
        events. The <tt class="literal">cancel-event</tt> types can be combined by specifying them in a
        comma separated list in the attribute.</p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="customizingtaskinstances"></a>11.10.&nbsp;定制任务实例</h2></div>
        </div>
        <div></div>
    </div>
    <p>Task instances can be customized. The easiest way to do this is
        to create a subclass of <tt class="literal">TaskInstance</tt>. Then create a
        <tt class="literal">org.jbpm.taskmgmt.TaskInstanceFactory</tt> implementation and
        configure it by setting the configuration property <tt class="literal">jbpm.task.instance.factory</tt>
        to the fully qualified class name in the jbpm.cfg.xml. If you use a subclass of
        TaskInstance, also create a hibernate
        mapping file for the subclass (using the hibernate
        <tt class="literal">extends="org.jbpm.taskmgmt.exe.TaskInstance"</tt>). Then
        add that mapping file to the list of mapping files in the
        <tt class="literal">hibernate.cfg.xml</tt></p></div>
<div class="section" lang="cn">
    <div class="titlepage">
        <div>
            <div><h2 class="title" style="clear: both"><a name="theidentitycomponent"></a>11.11.&nbsp;身份标识组件</h2></div>
        </div>
        <div></div>
    </div>
    <p>对用户、用户组和许可的管理就是我们通常所说的身份标识管理。jBPM包含了一个可选的身份表示组件，该组件也可以很容易
        地用公司自己的身份数据存储来代替。
    </p>

    <p>jBPM的身份标识管理组件包含有组织模型的知识。任务分派常常是利用组织模型的知识来进行进行的。所以这意味着
        一个组织的模型对用户，用户组、系统和他们之间的关系的描述。可选地，许可和角色也可以包含到组织模型中。
        很多学术研究的尝试都失败了，证明没有一个通用的组织模型可以适用于每一个组织。
    </p>

    <p>jBPM对这个的处理方式是通过定义角色（actor）为一个实际参与流程对象。角色是一个可以用其ID唯一标识的对象，
        即actorId。jBPM只有关于actorId的知识，该id用<tt class="literal">java.lang.String</tt>来表示
        以便可以尽量的灵活。所以任何关于组织模型和数据结构的知识都是在jBPM核心引擎之外的。</p>

    <p>作为jBPM扩展的一部分，我们将来会提供一个组件来管理简单的用户-角色模型。这种用户和角色间多对多的关系与
        J2EE和servlet标准中定义的是一样的，同时这可以作为新应用开发的起点。对contributing感兴趣的读者可以
        查查jboss jbpm的jira问题跟踪以便了解更加详细的信息。</p>

    <p>请注意用户-角色模型（和servlet, ejb和portlet标准中用到的一样）还不够强大来处理任务分派。这种模型只是
        用户和角色间多对多的关系，并没包括流程中用户团队和组织结构的信息。</p>

    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="theidentitymodel"></a>11.11.1.&nbsp;身份标识模型</h3></div>
            </div>
            <div></div>
        </div>
        <div class="figure"><a name="identity.model.image"></a>

            <div class="mediaobject" align="center"><img src="images/identity.model.gif" align="middle"
                                                         alt="The identity model class diagram"></div>
            <p class="title"><b>Figure&nbsp;11.3.&nbsp;身份标识模型类图</b></p></div>
        <p>黄色的类是和下面将会讨论的expression assignment处理器相关的。</p>

        <p><tt class="literal">User</tt>表示一个用户或服务。<tt class="literal">Group</tt>代表
            任何由用户组成的组。组可以嵌套来为团队，商业单位和整个公司的关系建模。group用类型来区分层级组
            和类似头发颜色这样的分组。<tt class="literal">Membership</tt>用来表示用户和组之间多对多
            的关系。membership可以用来表示公司里面的一个职位。membership的名字可以用来指示用户填写在
            group里的角色。</p></div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="assignmentexpressions"></a>11.11.2.&nbsp;指派表达式（Assignment expressions）</h3>
                </div>
            </div>
            <div></div>
        </div>
        <p>身份标识组件带有一个在任务指派期间计算角色（actor）的表达式的实现。这是一个在流程定义里面
            使用指派表达式的例子：</p>
        <pre class="programlisting">&lt;process-definition&gt;
  ...
  &lt;task-node name='a'&gt;
    &lt;task name='laundry'&gt;
      &lt;assignment expression='previous --&gt; group(hierarchy) --&gt; member(boss)' /&gt;
    &lt;/task&gt;
    &lt;transition to='b' /&gt;
  &lt;/task-node&gt;
  ...</pre>
        <p>指派表达式的语法是这样的：</p>
        <pre class="programlisting">first-term --&gt; next-term --&gt; next-term --&gt; ... --&gt; next-term

其中

first-term ::= previous |
               swimlane(swimlane-name) |
               variable(variable-name) |
               user(user-name) |
               group(group-name)

以及 

next-term ::= group(group-type) |
              member(role-name)
</pre>
        <div class="section" lang="cn">
            <div class="titlepage">
                <div>
                    <div><h4 class="title"><a name="firstterms"></a>11.11.2.1.&nbsp;第一个条件（First terms）</h4></div>
                </div>
                <div></div>
            </div>
            <p>表达式是从左到右进行解释的。first-term指定一个身份标识模型里的<tt class="literal">User</tt> 或
                <tt class="literal">Group</tt>。后续的terms从中间user或group再去计算下一个条件。</p>

            <p><tt class="literal">previous</tt>：表示任务指派给当前已认证的actor，即完成流程上一个步骤的actor.</p>

            <p><tt class="literal">swimlane(swimlane-name)</tt>：表示指定swimlane实例中的用户或组。</p>

            <p><tt class="literal">variable(variable-name)</tt>：表示指定实例变量中的用户或组。实例变量可以包含
                表示从身份标识组件获取的用户或组的标识字符串（<tt class="literal">java.lang.String</tt>），或包含
                <tt class="literal">User</tt> 对象 <tt class="literal">Group</tt> 对象。</p>

            <p><tt class="literal">user(user-name)</tt>：表示从身份标识组件获取的用户。</p>

            <p><tt class="literal">group(group-name)</tt>：表示从身份标识组件获取的组。</p></div>
        <div class="section" lang="cn">
            <div class="titlepage">
                <div>
                    <div><h4 class="title"><a name="nextterms"></a>11.11.2.2.&nbsp;下一个条件（Next terms）</h4></div>
                </div>
                <div></div>
            </div>
            <p><tt class="literal">group(group-type)</tt>：为用户获取用户的组。这意味着上一个条件的结果应该是一个
                <tt class="literal">User</tt>。这个条件为该用户在所有membership中搜索指定group-type的组。</p>

            <p><tt class="literal">member(role-name)</tt>：获取指定组中有指定角色的用户。前一个条件获得的结果必须
                是一个<tt class="literal">Group</tt>。该条件搜索组中有指定role-name的membership的用户。</p></div>
    </div>
    <div class="section" lang="cn">
        <div class="titlepage">
            <div>
                <div><h3 class="title"><a name="removingtheidentitycomponent"></a>11.11.3.&nbsp;移除身份标识组件</h3></div>
            </div>
            <div></div>
        </div>
        <p>当你想要使用自己的组织模型信息数据库（如自己公司的用户数据库或ldap系统）的时候，你可以很容易
            的去掉jBPM的身份标识组件。唯一需要做的事情是确认你已经从<tt class="literal">hibernate.cfg.xml</tt>
            中删除了这些行 ...</p>
        <pre class="programlisting">&lt;mapping resource="org/jbpm/identity/User.hbm.xml"/&gt;
&lt;mapping resource="org/jbpm/identity/Group.hbm.xml"/&gt;
&lt;mapping resource="org/jbpm/identity/Membership.hbm.xml"/&gt;</pre>

        <p><tt class="literal">ExpressionAssignmentHandler</tt>是依赖于身份标识组件的，所以你不能在直接使用他。
            如果想要重用<tt class="literal">ExpressionAssignmentHandler</tt>并绑定到自己的用户数据，你可以扩展
            <tt class="literal">ExpressionAssignmentHandler</tt>并重载<tt class="literal">getExpressionSession</tt>方法。
        </p>
        <pre class="programlisting">protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);</pre>
    </div>
</div>
</div>
<div class="navfooter">
    <hr>
    <table width="100%" summary="Navigation footer">
        <tr>
            <td width="40%" align="left"><a accesskey="p" href="context.html">Prev</a>&nbsp;</td>
            <td width="20%" align="center"><a accesskey="u" href="index.html">Up</a></td>
            <td width="40%" align="right">&nbsp;<a accesskey="n" href="scheduler.html">Next</a></td>
        </tr>
        <tr>
            <td width="40%" align="left" valign="top">Chapter&nbsp;10.&nbsp;Context&nbsp;</td>
            <td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td>
            <td width="40%" align="right" valign="top">&nbsp;Chapter&nbsp;12.&nbsp;Scheduler</td>
        </tr>
    </table>
</div>
</body>
</html>